import requests
import datetime as dt
import json


def get_dict_attr_or_None(_dict: dict, key, default=None):
    return _dict[key] if key in _dict.keys() else default


def get_api_infos(url):
    resp = requests.get(url)
    data = resp.json()
    definitions = get_dict_attr_or_None(data, 'definitions')
    paths = get_dict_attr_or_None(data, 'paths')
    tags = get_dict_attr_or_None(data, 'tags')
    return definitions, paths, tags


def convert_definitions(definitions: dict):
    def convert_definition(definition):
        if isinstance(definition, str) and definition.startswith('#/definitions/'):
            _ref = definition[len('#/definitions/'):]
            if _ref in definitions.keys():
                return convert_definition(definitions[_ref])
        if not isinstance(definition, dict):
            return definition
        result = {}
        for key in definition.keys():
            result[key] = convert_definition(definition[key])
        return result

    """将所有的定义补充完整"""
    results = {}
    for model_name in definitions.keys():
        results[model_name] = convert_definition(definitions[model_name])
    return results


def example(definition):
    result = {}
    properties = get_dict_attr_or_None(definition, 'properties', {})
    for pro in properties.keys():
        _ref = get_dict_attr_or_None(properties[pro], '$ref', None)
        _type = get_dict_attr_or_None(properties[pro], 'type', 'string')
        _example = get_dict_attr_or_None(properties[pro], 'example')
        if _example is not None:
            result[pro] = _example
        elif _ref is not None:
            result[pro] = example(_ref)
        elif _type == 'array':
            _items = get_dict_attr_or_None(properties[pro], 'items')
            if _items is not None:
                if '$ref' in _items.keys():
                    result[pro] = [example(_items['$ref'])]
        elif _type == 'boolean':
            result[pro] = False
        elif _type == 'string':
            _format = get_dict_attr_or_None(properties[pro], 'format')
            if 'date-time' == _format:
                result[pro] = dt.datetime.now().isoformat()
            else:
                result[pro] = '实例1'
        elif _type == 'integer':
            result[pro] = 0
    return result


def path_to_tag_dict(paths):
    paths = [(k, paths[k]) for k in paths.keys()]
    _tag_path = {}
    for k, v in paths:
        for m in v.keys():
            m_tags = get_dict_attr_or_None(v[m], 'tags', [])
            for tag in m_tags:
                infos = [] if tag not in _tag_path.keys() else _tag_path[tag]
                infos.append((k, m, v[m]))
                _tag_path[tag] = infos
    return _tag_path


def to_content(tag, tag_paths, definitions):
    content = {'title': tag, 'requests': []}
    for (path, method, info) in tag_paths[tag]:
        request = {'path': path, 'method': method, 'summary': get_dict_attr_or_None(info, 'summary', '')}
        content['requests'].append(request)
        request['query_params'] = []
        request['path_params'] = []
        for para in get_dict_attr_or_None(info, 'parameters', []):
            _in = get_dict_attr_or_None(para, 'in', 'query')
            _params = {'name': get_dict_attr_or_None(para, 'name', ''),
                       'description': get_dict_attr_or_None(para, 'description', ''),
                       'type': get_dict_attr_or_None(para, 'type', ''),
                       'required': get_dict_attr_or_None(para, 'required', False)
                       }
            if _in == 'query':
                request['query_params'].append(_params)
            elif _in == 'path':
                request['path_params'].append(_params)
            else:
                schema = get_dict_attr_or_None(para, 'schema')
                if schema is not None:
                    _ref = get_dict_attr_or_None(schema, '$ref', None)
                    if _ref is not None and _ref[len('#/definitions/'):] in definitions:
                        _defi = definitions[_ref[len('#/definitions/'):]]
                        request['body_params'] = {'model': _defi, 'example': example(_defi)}
        responses = get_dict_attr_or_None(info, 'responses')
        request['responses'] = []
        if responses is not None and '200' in responses.keys():
            schema = get_dict_attr_or_None(responses['200'], 'schema')
            if schema is not None:
                _ref = get_dict_attr_or_None(schema, '$ref', None)
                if _ref is not None and _ref[len('#/definitions/'):] in definitions:
                    _defi = definitions[_ref[len('#/definitions/'):]]
                    request['responses'].append({'code': '200', 'model': _defi, 'example': example(_defi)})
    return content


def from_api_to_info(url):
    defi, _paths, _tags = get_api_infos(url)
    definitions = convert_definitions(defi)
    tag_paths = path_to_tag_dict(_paths)
    for tag in _tags:
        if tag['name'] in tag_paths.keys():
            yield tag, to_content(tag['name'], tag_paths, definitions)


def _json_dump(json_data):
    return json.dumps(json_data, ensure_ascii=False, indent=2)


def to_markdown(content: dict):
    strs = [content['title'], '___']

    def _append_data(model, _example):
        strs.append('model模型\n\n')
        strs.append('```json\n')
        strs.append(_json_dump(model))
        strs.append('\n```\n')
        strs.append('示例:\n\n')
        strs.append('```json\n')
        strs.append(_json_dump(_example))
        strs.append('\n```\n')
    for request in content['requests']:
        strs.append("**%s %s\n\n**" % (request['method'].upper(), request['path']))
        strs.append("%s\n\n" % request['summary'])
        if len(request['query_params']) > 0:
            strs.append('**query参数**\n\n')
            strs.append('| 参数名 | 类型 | 描述 | 必须 |\n')
            strs.append('|-------|------|------|-----|\n')
            for param in request['query_params']:
                strs.append("| %s | %s | %s | %s |\n" % (
                  param['name'], param['type'], param['description'], param['required']
                ))
        if len(request['path_params']) > 0:
            strs.append('**path参数**\n\n')
            strs.append('| 参数名 | 类型 | 描述 | 必须 |\n')
            strs.append('|-------|------|------|-----|\n')
            for param in request['path_params']:
                strs.append("| %s | %s | %s | %s |\n" % (
                  param['name'], param['type'], param['description'], param['required']
                ))
        if 'body_params' in request.keys():
            strs.append('body参数\n\n')
            body_params = request['body_params']
            _append_data(body_params['model'], body_params['example'])

        for response in request['responses']:
            strs.append('返回数据\n\n')
            _append_data(response['model'], response['example'])
        strs.append('***\n')
        strs.append('\n')
        strs.append('\n')
    return strs


if __name__ == '__main__':
    for tag, _content in from_api_to_info('http://localhost:9899/mall/v2/api-docs'):
        with open('temp/%s.json' % tag['name'], mode='w', encoding='utf-8-sig') as f:
            json.dump(_content, f, ensure_ascii=False, indent=2)
        with open('temp/%s.md' % tag['name'], mode='w', encoding='utf-8-sig') as f:
            for line in to_markdown(_content):
                print(line, file=f)
